﻿namespace PlaylistCopy.Services.Impl
{
    using System.Management.Instrumentation;
    using PlaylistCopy.Helpers.Windows;
    using PlaylistCopy.Helpers.Windows.Implementations;
    using PlaylistCopy.ViewModels;
    using PlaylistCopy.Views;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows;

    internal class WindowFactory : IWindowFactory
    {
        private readonly HashSet<FrameworkElement> views;

        public WindowFactory()
        {
            views = new HashSet<FrameworkElement>();
        }

        public static ViewModelLocatorService LocatorService
        {
            get
            {
                return Application.Current.Resources["ViewModelLocator"] as ViewModelLocatorService;
            }
        }

        public ReadOnlyCollection<FrameworkElement> Views
        {
            get { return new ReadOnlyCollection<FrameworkElement>(views.ToList()); }
        }

        /// <summary>
        /// Registers a View.
        /// </summary>
        /// <param name="view">The registered View.</param>
        public void Register(FrameworkElement view)
        {
            var owner = GetOwner(view);
            if (owner == null)
            {
                view.Loaded += LateRegister;
                return;
            }

            owner.Closed += OwnerClosed;

            views.Add(view);
        }

        public void Unregister(FrameworkElement view)
        {
            views.Remove(view);
        }

        public ISummaryWindowOptions CreateSummaryWindow(object ownerViewModel)
        {
            var vm = LocatorService.SummaryWindowVm;
            var window = new SummaryWindow
                         {
                             DataContext = vm,
                             Owner = this.FindOwnerWindow(ownerViewModel)
                         };
            vm.CloseRequest = (sender, args) => window.Close();

            return new SummaryWindowOptions(window);
        }

        public IAboutWindowOptions CreateAboutWindow(object ownerViewModel)
        {
            var vm = LocatorService.AboutWindowVm;
            var window = new AboutWindow
                         {
                             DataContext = vm,
                             Owner = this.FindOwnerWindow(ownerViewModel)
                         };
            vm.CloseReqest += (sender, args) => window.Close();

            return new AboutWindowOptions(window);
        }

        #region Attached properties

        public static readonly DependencyProperty IsRegisteredViewProperty =
          DependencyProperty.RegisterAttached(
          "IsRegisteredView",
          typeof(bool),
          typeof(DialogService),
          new UIPropertyMetadata(IsRegisteredViewPropertyChanged));

        public static bool GetIsRegisteredView(FrameworkElement target)
        {
            return (bool)target.GetValue(IsRegisteredViewProperty);
        }

        public static void SetIsRegisteredView(FrameworkElement target, bool value)
        {
            target.SetValue(IsRegisteredViewProperty, value);
        }

        private static void IsRegisteredViewPropertyChanged(DependencyObject target,
          DependencyPropertyChangedEventArgs e)
        {
            if (DesignerProperties.GetIsInDesignMode(target))
            {
                return;
            }

            var view = target as FrameworkElement;
            if (view == null)
            {
                return;
            }
            if (LocatorService == null)
            {
                return;
            }

            var newValue = (bool)e.NewValue;

            if (newValue)
            {
                LocatorService.Resolve<IWindowFactory>().Register(view);
            }
            else
            {
                LocatorService.Resolve<IWindowFactory>().Unregister(view);
            }
        }

        #endregion

        private static Window GetOwner(DependencyObject view)
        {
            return view as Window ?? Window.GetWindow(view);
        }

        private Window FindOwnerWindow(object viewModel)
        {
            var view =
              views.SingleOrDefault(v => ReferenceEquals(v.DataContext, viewModel));
            if (view == null)
            {
                throw new ArgumentException("Viewmodel is not referenced by any registered View.");
            }

            var owner = view as Window ?? Window.GetWindow(view);
            if (owner == null)
            {
                throw new InvalidOperationException("View is not contained within a Window.");
            }

            return owner;
        }

        private void LateRegister(object sender, RoutedEventArgs e)
        {
            var view = sender as FrameworkElement;
            if (view == null)
            {
                return;
            }

            view.Loaded -= this.LateRegister;

            this.Register(view);
        }

        private void OwnerClosed(object sender, EventArgs e)
        {
            var owner = sender as Window;
            if (owner == null)
            {
                
                return;
            }

            var windowViews =
                from view in this.views
                // ReSharper disable once PossibleUnintendedReferenceComparison
                where Window.GetWindow(view) == owner
                select view;

            foreach (var view in windowViews.ToArray())
            {
                this.Unregister(view);
            }
        }
    }
}
